<?php
/**
 * Groo generic metadata fetch code.
 *
 * $Horde: incubator/groo/lib/Fetch.php,v 1.5 2006/08/14 04:12:35 luciano Exp $
 *
 * Copyright 2004-2006 Luciano Ramalho <luciano@ramalho.org>
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Luciano Ramalho <luciano@ramalho.org>
 * @package Groo
 */


#error_reporting(E_ALL); assert_options(ASSERT_ACTIVE,1);
#assert_options(ASSERT_WARNING,1); assert_options(ASSERT_BAIL,1);

require_once('PEAR.php');
require_once("DB.php");

require_once('Book.php');
require_once('ISBN.php');
require_once('Fetch/Amazon.php');

define('SUCCESS','SUCCESS');
define('NOT_FOUND','NOT_FOUND');
define('INVALID_AWS_KEY','INVALID_AWS_KEY');
define('NETWORK_ERROR','NETWORK_ERROR');
define('UNKNOWN_AMAZON_ERROR','UNKNOWN_AMAZON_ERROR');
define('METADATA_FORMAT_ERROR','METADATA_FORMAT_ERROR');

class FetchException extends Exception { };
class FetchNetworkException extends FetchException { };

/*** metadata parsing utilities ***/

function getSingleElementValue($doc, $tagName, $required=0)
{
    $items = $doc->getElementsByTagName($tagName);
    if ($items->length==1) { 
        return $items->item(0)->nodeValue;
    } elseif ($items->length==0) {
        if ($required) { 
            throw new AmazonException("Required element '$tagName' not found\n".
                $doc->saveXML());
        } else {
            return NULL;
        };
    } else {
        throw new AmazonException("More than one element '$tagName' found (total: $items->length)");
    }
}

function removeExtraSpaces($string)
{
    return implode(' ',preg_split("/\s+/",trim($string))); 
}

function getQueue($db, $source, $param, $status=NULL, $max_attempts=3)
{
    $sql = "SELECT query_id, param_value FROM groo_remote_queries"
            ." WHERE 1=1 "
            ." AND metadata_source = ?"
            ." AND param_name = ?"
            ." AND last_attempt_num <= ?"
            ;
    if (is_null($status)) {
        $sql .= " AND ISNULL(query_status)";
    } else {
        $sql .= " AND query_status = ". $db->quoteSmart($status);
    }

    $qry = $db->prepare($sql);
    $res = $db->execute($qry, array($source, $param, $max_attempts));
    if (PEAR::isError($res)) { die($res->getMessage()); }
    //echo $res->numRows(). " rows\n--------\n";
    $queue = array();
    while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
        $queue[$row['query_id']] = $row['param_value'];
    }
    return $queue;
}

function getUntitled($db)
{
    $sql = "SELECT isbn13 FROM groo_books"
            ." WHERE ISNULL(title)";
    $res = $db->query($sql);
    if (PEAR::isError($res)) { die($res->getMessage()); }
    $queue = array();
    while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
        $queue[] = $row['isbn13'];
    }
    return $queue;
}

/**
 * Get untitled books newer than the most recent queue update
 *
 * @access public
 */
function getNewUntitled($db)
{
    $sql = 'SELECT max(created_dt) FROM '
        . '   groo_remote_queries';

    $res = $db->getOne($sql);
    if (PEAR::isError($res)) { var_export($res); die(); }
    if (is_null($res)) {
        $created_clause = '';
    } else {
        $created_clause = ' AND created_dt >= '.$db->quote($res);
    }
    $sql = 'SELECT isbn13 FROM groo_books'
        . ' WHERE ISNULL(title) ' . $created_clause; 
    $res = $db->query($sql);
    if (PEAR::isError($res)) { var_export($res); die(); }
    $queue = array();
    while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
        $queue[] = $row['isbn13'];
    }
    return $queue;
}

function updateQuery($db, $query_id, $status, $message)
{
    $sql = "UPDATE groo_remote_queries"
            ." SET last_attempt_num = last_attempt_num+1,"
            ." query_status = ?, message = ?"
            ." WHERE query_id = ?"
            ;
    $qry = $db->prepare($sql);
    $res = $db->execute($qry, array($status, $message, $query_id));
    if (PEAR::isError($res)) { die($res->getMessage()); }    
}

function updateItems($db, $bookId, $title)
{
    $sql = "UPDATE groo_items"
            ." SET item_desc = ?"
            ." WHERE book_id = ?"
            ;
    $qry = $db->prepare($sql);
    $res = $db->execute($qry, array($title, $bookId));
    if (PEAR::isError($res)) { die($res->getMessage()); }    
}

function getConfigDSN()
{
    /* FIXME: this is an ugly hack; I need to find out how to properly
      read values of the Horde configuration from a CLI script like this one,
      which will never be called through HTTP but will be a cron job
    */
    $conf_lines = array();
    $conf = array('sql'=>array());
    foreach (file('../../config/conf.php') as $lin) {
        $pos = strpos($lin, "['sql']");
        if ($pos !== false) {
            eval($lin);
        }
    }
    # DSN='mysql://user:passwd@host:port/db'
    $dsn = $conf['sql']['phptype'].'://';
    $dsn .= $conf['sql']['username'].':'.$conf['sql']['password'];
    $dsn .= '@'.$conf['sql']['hostspec'].':'.$conf['sql']['port'];
    $dsn .= '/'.$conf['sql']['database'];
    return $dsn;
}

function queueISBN($db,$isbn13)
{
    /* FIXME: this needs refactoring; it is totally Amazon-dependent
      so should go into de Amazon.php subclass */
    $sql =  "INSERT INTO groo_remote_queries"
            ." (created_dt, metadata_source, param_name, param_value)"
            ." VALUES (NOW(), ?, ?, ?)"
            ;
    $cmd = $db->prepare($sql);
    $asin = ISBN::convertEANtoISBN($isbn13);
    $args = array('amazon.com', 'isbn', $asin);
    $res = $db->execute($cmd,$args);
    return $res;
}

function queueUntitled($db)
{
    $queue = getNewUntitled($db);
    $queued_count = 0;
    foreach ($queue as $isbn) {
        $res = queueISBN($db, $isbn);
        if (PEAR::isError($res)) {
            if (strpos($res->message, 'already exists')===false) {
                throw new FetchException($res->message);
            }
        } else {
            ++$queued_count;
        }
    } // foreach
    return $queued_count;
}

function writeISBNimages($isbn,$medSize=240) {
    $base = '../images';
    if (strlen($isbn)==10) {
        $filename=ISBN::convertISBN10toISBN13($isbn);
    } else {
        $filename='_'.$isbn;    
    }
    $pathLarge = "$base/large/$filename.jpg";
    if (file_exists($pathLarge )) {
        echo $isbn." existed\n";
        return;
    }
    try {
        $img = fetchISBNimage($isbn);
    } catch (AmazonException $exc) {
        echo $exc->getMessage()."\n";
        return;
    }
    $fp = fopen($pathLarge, 'w');
    if ($fp) {
        fwrite($fp, $img);
        fclose($fp);
    } else {
        throw new AmazonException(__FILE__.":fetchISBNimage: Unable to write to local path '$pathLarge'");
    }
    if ($medSize) {
        $width = $medSize;
        $height = $medSize;
        list($width_orig, $height_orig) = getimagesize($pathLarge);
        if ($width && ($width_orig < $height_orig)) {
           $width = ($height / $height_orig) * $width_orig;
        } else {
           $height = ($width / $width_orig) * $height_orig;
        }
        $image_p = imagecreatetruecolor($width, $height);
        $image = imagecreatefromjpeg($pathLarge);
        imagecopyresampled($image_p, $image, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);
        $pathMedium = "$base/medium/$filename.jpg";
        imagejpeg($image_p, $pathMedium, 75);
    };
    echo $isbn."\n";
}

function fetchStatus($db, $status, $withImage=true)
{
    $queue = getQueue($db, 'amazon.com', 'isbn', $status);
    $queue_len = count($queue);
    $success_count = 0;
    foreach ($queue as $query_id=>$isbn) {
        $status = NULL;
        $msg = NULL;
        $metadata = NULL;
        try {
            $metadata = fetchISBN($isbn);
        } catch (AmazonException $exc) {
            $msg = $exc->getMessage();
            if (is_numeric(strpos($msg,'AWS.InvalidParameterValue'))) {
                if (is_numeric(strpos($msg,'not a valid value for AWSAccessKeyId'))) {
                    throw new AmazonException("Fetch.php: ".$msg);
                } else {
                    $status = NOT_FOUND;
                }
            } else {
                $status = UNKNOWN_AMAZON_ERROR;
            }
        } catch (FetchNetworkException $exc) {
            $msg = $exc->getMessage();
            $status = NETWORK_ERROR;
        }
        if (!is_null($metadata)) {
            try {
                $book = new Book($metadata);
                $book->db_update($db, 'amazon.com');
                updateItems($db, $book->recordId, $book->getTile());
            } catch (FetchException $exc) {
                $msg = $exc->getMessage();
                $status = METADATA_FORMAT_ERROR;
            }
            if (is_null($status)) {
                $status = SUCCESS;
                $success_count++;
            }
        }
        updateQuery($db, $query_id, $status, $msg);
        if ($withImage) {
            writeISBNimages($isbn);
        }
    } // foreach
    return "$queue_len items were queued; $success_count succesfully retrieved\n";  
}

function fetchImagesExisting($db, $status)
{
    $queue = getQueue($db, 'amazon.com', 'isbn', $status);
    $queue_len = count($queue);
    $success_count = 0;
    foreach ($queue as $query_id=>$isbn) {
        $status = NULL;
        $msg = NULL;
        $metadata = NULL;
        writeISBNimages($isbn);
        echo $isbn."\n";
        updateQuery($db, $query_id, 'HAS_IMAGE', $msg);
    } // foreach
    return "$queue_len items were queued; $success_count succesfully retrieved\n";  
}

/*** fetch items with a certain status in table groo_remote_queries ***/
if (isset($argc) and $argc==3 and $argv[1]=='--with-status') {
    $status = $argv[2];
    if ($status == 'NULL') { $status = NULL; }
    $db = DB::connect(getConfigDSN(), array());
    $db->setOption('debug', 2);
    if (PEAR::isError($db)) { die($db->getMessage()); }
    echo fetchStatus($db, $status);

} elseif (isset($argc) and $argc==2 and $argv[1]=='--empty-queue') {
    $db = DB::connect(getConfigDSN(), array());
    $db->setOption('debug', 2);
    if (PEAR::isError($db)) { die($db->getMessage()); }
    $res = $db->query("DELETE FROM groo_remote_queries");
    if (PEAR::isError($res)) { die($res->getMessage()); }
    echo "groo_remote_queries emptied\n";
} elseif (isset($argc) and $argc==2 and $argv[1]=='--q-untitled')  {
    $db = DB::connect(getConfigDSN(), array());
    $db->setOption('debug', 2);
    if (PEAR::isError($db)) { die($db->getMessage()); }
    echo queueUntitled($db)." items were queued\n";
} elseif (isset($argc) and $argc==2 and $argv[1]=='--fetch-untitled')  {
    $db = DB::connect(getConfigDSN(), array());
    $db->setOption('debug', 2);
    if (PEAR::isError($db)) { die($db->getMessage()); }
    $untitled = queueUntitled($db);
    if ($untitled) {
        echo fetchStatus($db, NULL);
    } 
} elseif (isset($argc) and $argc==2 and $argv[1]=='--fetch-images')  {
    $db = DB::connect(getConfigDSN(), array());
    $db->setOption('debug', 2);
    if (PEAR::isError($db)) { die($db->getMessage()); }
    echo fetchImagesExisting($db, 'SUCCESS');
} elseif (isset($argc) and $argc==2 and $argv[1]=='--test-creators') {
    $doc = new DOMDocument();
    $doc->loadXML(file_get_contents('Fetch/amazon_xml/0393048470.xml'));
    $data = parseItemAttributes($doc);
    var_export($data);
} elseif (isset($argc) and $argc==2 and $argv[1]=='--test-images') {
    $alice = '0393048470'; # multiple images
    $itsMagic = '8508040210'; # no image available
    $ltHornblower = '0316290637'; # search inside
    $gof = '0201633612'; # just one image
    $wfdt = '0226468046'; # one image
    $isbn = $wfdt;
    writeISBNimages($isbn);

}    


?>
